import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'dart:async';

/// 卡片轮播控制器，用于外部控制卡片切换
///
/// 提供了一系列方法用于外部代码控制卡片轮播，包括页面跳转、自动播放控制等功能。
/// 通过[CardBanner.controller]属性将控制器与轮播组件关联。
class CardBannerController {
  _CardBannerState? _state;

  /// 将控制器与卡片轮播状态绑定
  void _attach(_CardBannerState state) {
    _state = state;
  }

  /// 解除控制器与卡片轮播状态的绑定
  void _detach() {
    _state = null;
  }

  /// 跳转到指定页面（无动画）
  ///
  /// [page] - 目标页面索引，从0开始
  void jumpToPage(int page) {
    _state?._jumpToPage(page);
  }

  /// 翻到下一页
  ///
  /// [animated] - 是否使用动画，默认为true
  /// 返回一个Future，动画完成后完成
  Future<void> nextPage({bool animated = true}) async {
    return _state?._goToNextPage(animated) ?? Future.value();
  }

  /// 翻到上一页
  ///
  /// [animated] - 是否使用动画，默认为true
  /// 返回一个Future，动画完成后完成
  Future<void> previousPage({bool animated = true}) async {
    return _state?._goToPreviousPage(animated) ?? Future.value();
  }

  /// 翻到指定页面
  ///
  /// [page] - 目标页面索引，从0开始
  /// [animated] - 是否使用动画，默认为true
  /// 返回一个Future，动画完成后完成
  Future<void> animateToPage(int page, {bool animated = true}) async {
    return _state?._animateToPage(page, animated) ?? Future.value();
  }

  /// 启动自动播放
  ///
  /// 无论当前自动播放状态如何，都会将自动播放设置为启用状态
  void startAutoPlay() {
    _state?._startAutoPlay(true);
  }

  /// 停止自动播放
  ///
  /// 停止当前的自动播放并取消自动播放计时器
  void stopAutoPlay() {
    _state?._stopAutoPlay();
  }

  /// 获取当前页面索引
  int? get currentPage => _state?._currentPage;
}

/// 卡片Banner组件，支持横向切换、自动轮播、手势操作
/// 以卡片垂直中轴线为旋转轴的3D翻转效果
///
/// 特性：
/// - 支持手势拖动翻页
/// - 支持自动轮播
/// - 支持3D翻转动画效果
/// - 支持循环播放
/// - 支持自定义卡片正反面内容
/// - 提供外部控制接口
class CardBanner extends StatefulWidget {
  /// 轮播的子项目（卡片）
  ///
  /// 这些Widget将作为卡片的正面内容展示
  final List<Widget> children;

  /// 轮播的背面子项目（可选）
  ///
  /// 如果提供，则每个卡片的背面将使用对应的背面项目；
  /// 如果不提供，则所有卡片使用同一种翻转效果
  /// 如果提供，必须与[children]长度相同
  final List<Widget>? backSideChildren;

  /// 是否循环播放
  ///
  /// 当为true时，最后一页翻到下一页会回到第一页，
  /// 第一页翻到上一页会到最后一页
  final bool loop;

  /// 是否自动播放
  ///
  /// 当为true时，卡片会自动按照[duration]指定的时间间隔翻页
  final bool autoPlay;

  /// 每个卡片停留的时间（毫秒）
  ///
  /// 仅当[autoPlay]为true时有效
  final int duration;

  /// 切换动画的时间（毫秒）
  ///
  /// 控制卡片翻转动画的持续时间
  final int animationDuration;

  /// 卡片的宽度，默认为null（撑满容器）
  final double? cardWidth;

  /// 卡片的高度，默认为null（撑满容器）
  final double? cardHeight;

  /// 卡片的边距
  final EdgeInsetsGeometry? padding;

  /// 页面变化回调
  ///
  /// 当页面切换完成时调用，参数为新的页面索引
  final Function(int)? onPageChanged;

  /// 控制器，用于外部控制卡片翻转
  ///
  /// 通过此控制器可以从外部控制卡片的翻页和自动播放
  final CardBannerController? controller;

  /// 创建一个卡片Banner组件
  ///
  /// [children] 必须提供至少一个子Widget作为卡片内容
  /// [backSideChildren] 可选，提供卡片背面的内容
  /// [loop] 是否循环播放，默认为true
  /// [autoPlay] 是否自动播放，默认为true
  /// [duration] 每个卡片停留的时间（毫秒），默认为3000毫秒
  /// [animationDuration] 切换动画的时间（毫秒），默认为800毫秒
  const CardBanner({
    Key? key,
    required this.children,
    this.backSideChildren,
    this.loop = true,
    this.autoPlay = true,
    this.duration = 3000,
    this.animationDuration = 800,
    this.cardWidth,
    this.cardHeight,
    this.padding,
    this.onPageChanged,
    this.controller,
  })  : assert(
            backSideChildren == null ||
                backSideChildren.length == children.length,
            'backSideChildren must have the same length as children'),
        super(key: key);

  @override
  _CardBannerState createState() => _CardBannerState();
}

class _CardBannerState extends State<CardBanner> with TickerProviderStateMixin {
  late AnimationController _animationController; // 翻转动画控制器
  Timer? _autoPlayTimer; // 自动播放计时器
  bool _autoPlayEnabled = false; // 自动播放是否启用
  bool _isPanning = false; // 是否正在拖动
  bool _isAnimating = false; // 是否正在动画
  int _currentPage = 0; // 当前页面索引
  late int _nextPage = 0; // 下一页面索引
  double _dragPosition = 0.0; // 拖动位置
  double _dragPercent = 0.0; // 拖动百分比
  bool _originalAutoPlayState = false; // 原始自动播放状态，用于记住手动交互前是否开启自动播放
  bool _shouldRestoreAutoPlayAfterAnimation = false; // 动画完成后是否恢复自动播放

  @override
  void initState() {
    super.initState();

    _autoPlayEnabled = widget.autoPlay;
    _originalAutoPlayState = widget.autoPlay;

    _animationController = AnimationController(
      // 使用系统vsync确保动画同步，避免掉帧

      duration: Duration(milliseconds: widget.animationDuration),
      vsync: this,
    );

    // 如果提供了控制器，将控制器与状态绑定
    if (widget.controller != null) {
      widget.controller!._attach(this);
    }

    if (_autoPlayEnabled) {
      _startAutoPlay(false);
    }

    _animationController.addListener(() {
      setState(() {});
    });

    _animationController.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        setState(() {
          // 动画完成后更新当前页
          _currentPage = _nextPage;
          _isPanning = false;
          _isAnimating = false;
          _dragPercent = 0.0;
          _dragPosition = 0.0;
        });
        _animationController.reset();

        if (widget.onPageChanged != null) {
          widget.onPageChanged!(_currentPage);
        }

        // 检查是否需要恢复自动播放
        if (_shouldRestoreAutoPlayAfterAnimation) {
          _autoPlayEnabled = true;
          _shouldRestoreAutoPlayAfterAnimation = false; // 重置标志
          _startAutoPlay(false);
        }
        // 如果是自动播放，继续下一个循环
        else if (_autoPlayEnabled) {
          _startAutoPlay(false);
        }
      }
    });
  }

  @override
  void didUpdateWidget(CardBanner oldWidget) {
    super.didUpdateWidget(oldWidget);

    // 处理控制器变更
    if (oldWidget.controller != widget.controller) {
      if (oldWidget.controller != null) {
        oldWidget.controller!._detach();
      }
      if (widget.controller != null) {
        widget.controller!._attach(this);
      }
    }

    if (oldWidget.autoPlay != widget.autoPlay) {
      _autoPlayEnabled = widget.autoPlay;
      _originalAutoPlayState = widget.autoPlay;
      if (_autoPlayEnabled) {
        _startAutoPlay(false);
      } else {
        _stopAutoPlay();
      }
    }

    if (oldWidget.duration != widget.duration) {
      if (_autoPlayEnabled) {
        _startAutoPlay(false);
      }
    }

    if (oldWidget.animationDuration != widget.animationDuration) {
      _animationController.duration =
          Duration(milliseconds: widget.animationDuration);
    }
  }

  /// 跳转到指定页面（无动画）
  ///
  /// [page] - 目标页面索引
  /// 如果页面无效或已在当前页面，则不执行操作
  void _jumpToPage(int page) {
    if (page < 0 ||
        page >= widget.children.length ||
        page == _currentPage ||
        _isAnimating) {
      return;
    }

    setState(() {
      _currentPage = page;
      if (widget.onPageChanged != null) {
        widget.onPageChanged!(_currentPage);
      }
    });
  }

  /// 使用动画切换到指定页面
  ///
  /// [page] - 目标页面索引
  /// [animated] - 是否使用动画
  ///
  /// 如果不使用动画，则直接调用[_jumpToPage]
  /// 否则启动翻转动画切换到目标页面
  Future<void> _animateToPage(int page, bool animated) async {
    if (page < 0 ||
        page >= widget.children.length ||
        page == _currentPage ||
        _isAnimating) {
      return;
    }

    if (!animated) {
      _jumpToPage(page);
      return;
    }

    _nextPage = page;
    _isAnimating = true;

    // 根据当前页和目标页的关系确定翻转方向
    return _animationController.forward(from: 0.0).orCancel.catchError((e) {
      // 忽略可能的动画取消异常
      return null;
    });
  }

  /// 翻到下一页
  ///
  /// [animated] - 是否使用动画
  ///
  /// 如果已经是最后一页：
  /// - 如果[loop]为true，则回到第一页
  /// - 如果[loop]为false，则不执行操作
  Future<void> _goToNextPage(bool animated) async {
    int nextPage = _currentPage + 1;
    if (nextPage >= widget.children.length) {
      if (widget.loop) {
        nextPage = 0;
      } else {
        return;
      }
    }

    return _animateToPage(nextPage, animated);
  }

  /// 翻到上一页
  ///
  /// [animated] - 是否使用动画
  ///
  /// 如果已经是第一页：
  /// - 如果[loop]为true，则跳到最后一页
  /// - 如果[loop]为false，则不执行操作
  Future<void> _goToPreviousPage(bool animated) async {
    int prevPage = _currentPage - 1;
    if (prevPage < 0) {
      if (widget.loop) {
        prevPage = widget.children.length - 1;
      } else {
        return;
      }
    }

    return _animateToPage(prevPage, animated);
  }

  /// 启动自动播放
  ///
  /// [fromExternal] - 是否来自外部调用
  ///
  /// 如果来自外部调用，则强制设置自动播放为启用状态
  /// 如果有以下情况之一，则不启动自动播放：
  /// - 自动播放已禁用
  /// - 卡片数量小于等于1
  /// - 正在拖动中
  /// - 正在动画中
  void _startAutoPlay(bool fromExternal) {
    if (fromExternal) {
      _autoPlayEnabled = true;
      _originalAutoPlayState = true;
    }

    if (!_autoPlayEnabled ||
        widget.children.length <= 1 ||
        _isPanning ||
        _isAnimating) {
      return;
    }

    // 取消之前的计时器
    _autoPlayTimer?.cancel();

    // 创建新的计时器
    _autoPlayTimer = Timer(Duration(milliseconds: widget.duration), () {
      if (mounted && !_isPanning && !_isAnimating && _autoPlayEnabled) {
        _goToNextPage(true);
      }
    });
  }

  /// 停止自动播放
  ///
  /// 设置自动播放为禁用状态并取消计时器
  void _stopAutoPlay() {
    _autoPlayEnabled = false;
    // 取消计时器
    _autoPlayTimer?.cancel();
  }

  /// 处理拖动开始事件
  ///
  /// 保存当前自动播放状态，暂停自动播放
  /// 设置拖动相关状态，准备处理拖动
  void _onPanStart(DragStartDetails details) {
    if (_isAnimating) {
      return;
    }

    // 保存当前自动播放状态，而不是直接停止
    bool wasAutoPlaying = _autoPlayEnabled;

    // 暂停自动播放，但不改变 _originalAutoPlayState
    if (_autoPlayEnabled) {
      _stopAutoPlay();
    }

    setState(() {
      _isPanning = true;
      _dragPosition = 0.0;
      _dragPercent = 0.0;
      // 确保 _originalAutoPlayState 正确记录原始状态
      _originalAutoPlayState = wasAutoPlaying || _originalAutoPlayState;
    });
  }

  /// 处理拖动更新事件
  ///
  /// 根据拖动距离计算拖动百分比，用于显示实时翻转效果
  /// 反转手势方向，使向左拖动显示下一页，向右拖动显示上一页
  void _onPanUpdate(DragUpdateDetails details) {
    if (_isAnimating || !_isPanning) {
      return;
    }

    // 计算拖动位置和百分比
    final screenWidth = MediaQuery.of(context).size.width;
    // 修正手势方向: 向左拖动时为负值，向右拖动时为正值
    // 但我们希望向左拖动时显示下一页（正向翻转），向右拖动时显示上一页（反向翻转）
    // 因此这里需要反转符号
    _dragPosition -= details.delta.dx; // 反转手势方向

    // 限制百分比为-1到1之间
    _dragPercent = (_dragPosition / (screenWidth / 2)).clamp(-1.0, 1.0);

    setState(() {});
  }

  /// 处理拖动结束事件
  ///
  /// 根据拖动百分比决定是否切换页面：
  /// - 如果拖动超过阈值(50%)，则切换到目标页面并启动动画
  /// - 如果拖动未超过阈值，则恢复到当前页面
  ///
  /// 在适当的时机恢复自动播放（如果原来是自动播放状态）
  void _onPanEnd(DragEndDetails details) {
    if (_isAnimating || !_isPanning) {
      return;
    }

    // 在任何情况下都保存原始自动播放状态以便最后恢复
    final bool shouldRestoreAutoPlay = _originalAutoPlayState;

    // 如果拖动百分比超过阈值，进行页面切换
    if (_dragPercent.abs() > 0.5) {
      // 避免状态更新导致界面重建
      int targetPage = _currentPage;

      if (_dragPercent < 0) {
        // 向右拖动，切换到上一页
        targetPage = _currentPage - 1;
        if (targetPage < 0) {
          if (widget.loop) {
            targetPage = widget.children.length - 1;
          } else {
            targetPage = 0;

            // 在边界情况下使用单次setState，减少重绘
            setState(() {
              _isPanning = false;
              _dragPercent = 0.0;
              _dragPosition = 0.0;

              // 恢复自动播放（如果需要）
              if (shouldRestoreAutoPlay) {
                _autoPlayEnabled = true;
                // 延迟启动自动播放，避免与动画冲突
                Future.microtask(() => _startAutoPlay(false));
              }
            });
            return;
          }
        }
      } else {
        // 向左拖动，切换到下一页
        targetPage = _currentPage + 1;
        if (targetPage >= widget.children.length) {
          if (widget.loop) {
            targetPage = 0;
          } else {
            targetPage = widget.children.length - 1;

            // 在边界情况下使用单次setState，减少重绘
            setState(() {
              _isPanning = false;
              _dragPercent = 0.0;
              _dragPosition = 0.0;

              // 恢复自动播放（如果需要）
              if (shouldRestoreAutoPlay) {
                _autoPlayEnabled = true;
                // 延迟启动自动播放，避免与动画冲突
                Future.microtask(() => _startAutoPlay(false));
              }
            });
            return;
          }
        }
      }

      // 核心修改：不再使用复杂的3D翻转动画，而是采用类似jumpToPage的简单过渡
      // 这样可以大幅提高性能，避免卡顿现象

      // 简化状态更新，直接切换到目标页面
      setState(() {
        _currentPage = targetPage;
        _isPanning = false;
        _isAnimating = false;
        _dragPercent = 0.0;
        _dragPosition = 0.0;

        // 通知页面变化
        if (widget.onPageChanged != null) {
          widget.onPageChanged!(_currentPage);
        }

        // 恢复自动播放（如果需要）
        if (shouldRestoreAutoPlay) {
          _autoPlayEnabled = true;
          // 使用微任务确保在当前事件循环结束后再启动自动播放
          Future.microtask(() => _startAutoPlay(false));
        }
      });
    } else {
      // 如果拖动百分比小于阈值，恢复当前页面
      setState(() {
        _isPanning = false;
        _dragPercent = 0.0;
        _dragPosition = 0.0;
        _nextPage = _currentPage;
      });

      // 手动控制结束后，根据原始自动播放状态决定是否恢复自动播放
      if (shouldRestoreAutoPlay) {
        _autoPlayEnabled = true; // 明确设置为启用
        // 延迟启动自动播放，避免与状态更新冲突
        Future.microtask(() => _startAutoPlay(false));
      }
    }
  }

  @override
  void dispose() {
    // 解除控制器绑定
    if (widget.controller != null) {
      widget.controller!._detach();
    }
    // 取消自动播放计时器
    _autoPlayTimer?.cancel();
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onHorizontalDragStart: (widget.children.length > 1) ? _onPanStart : null,
      onHorizontalDragUpdate:
          (widget.children.length > 1) ? _onPanUpdate : null,
      onHorizontalDragEnd: (widget.children.length > 1) ? _onPanEnd : null,
      // 处理可能的手势取消事件
      onHorizontalDragCancel: () {
        if (_isPanning && !_isAnimating) {
          setState(() {
            _isPanning = false;
            _dragPercent = 0.0;
            _dragPosition = 0.0;
          });

          if (_originalAutoPlayState) {
            _autoPlayEnabled = true;
            _startAutoPlay(false);
          }
        }
      },
      child: Container(
        width: widget.cardWidth,
        height: widget.cardHeight,
        padding: widget.padding,
        child: Stack(
          children: [
            _buildFlipCard(),
          ],
        ),
      ),
    );
  }

  /// 构建翻转卡片效果
  ///
  /// 根据当前状态（拖动中或动画中）计算卡片旋转角度
  /// 根据旋转角度决定显示正面还是背面内容
  /// 实现3D翻转效果
  Widget _buildFlipCard() {
    // 在拖动或动画中，显示当前页和下一页
    final int frontIndex = _currentPage;

    // 确定下一页，可能来自用户拖动或自动播放
    final int backIndex = _isPanning
        ? (_dragPercent < 0 // 反转条件判断
            ? (_currentPage > 0
                ? _currentPage - 1
                : (widget.loop ? widget.children.length - 1 : 0))
            : (_currentPage < widget.children.length - 1
                ? _currentPage + 1
                : (widget.loop ? 0 : widget.children.length - 1)))
        : _nextPage;

    // 获取当前显示的正面和背面Widget
    final Widget frontWidget = widget.children[frontIndex];

    // 如果有提供背面，则使用对应背面；否则使用下一页作为背面
    final Widget backWidget = widget.backSideChildren != null
        ? widget.backSideChildren![backIndex]
        : widget.children[backIndex];

    // 计算旋转角度
    double rotationY = 0.0;

    if (_isPanning) {
      // 拖动过程中，根据拖动百分比计算旋转角度
      rotationY = _dragPercent * math.pi; // 将范围扩大到 -π 到 π
    } else if (_isAnimating) {
      // 动画过程中，根据动画值计算旋转角度
      final double animationValue = _animationController.value;
      final bool isForward = _nextPage > _currentPage ||
          (_currentPage == widget.children.length - 1 &&
              _nextPage == 0); // 循环情况

      if (isForward) {
        // 向前翻转（当前页到下一页）
        rotationY = animationValue * math.pi; // 0 到 π
      } else {
        // 向后翻转（当前页到上一页）
        rotationY = -animationValue * math.pi; // 0 到 -π
      }
    }

    // 判断显示内容，应该根据角度决定显示正面还是背面
    // 当|rotationY|在[0, π/2)时显示正面，[π/2, π]时显示背面
    final bool showingBackSide = rotationY.abs() > math.pi / 2;

    // 使用RepaintBoundary隔离绘制，提高性能
    return RepaintBoundary(
      child: Center(
        child: Container(
          width: widget.cardWidth,
          height: widget.cardHeight,
          child: Transform(
            alignment: Alignment.center,
            transform: Matrix4.identity()
              ..setEntry(3, 2, 0.001) // 透视效果
              ..rotateY(rotationY), // 完整旋转
            child: showingBackSide
                ? Transform(
                    alignment: Alignment.center,
                    transform: Matrix4.identity()
                      ..rotateY(math.pi), // 背面需要再旋转180度，保证文字等正向显示
                    child: backWidget,
                  )
                : frontWidget,
          ),
        ),
      ),
    );
  }
}
